<?php

namespace app\common\helper;

use think\facade\Db;

class TreeHelper
{
    public static function findParentIds(
        int    $pid,                        // 当前节点PID
        string $tableName,              // 表名
        string $separator = ',',        // 分隔符
        string $primaryKeyField = 'id', // 主键字段
        string $parentIdField = 'pid'   // 父级ID字段
    ): string
    {
        if ($pid <= 0) {
            return '';
        }

        $ids = [];
        $currentId = $pid;
        while ($currentId > 0) {
            // 将当前节点(即上一层父级)放入结果中
            array_unshift($ids, $currentId);
            // 向上查找其父级
            $parentId = Db::name($tableName)
                ->where($primaryKeyField, '=', $currentId)
                ->value($parentIdField);

            if ($parentId && $parentId > 0) {
                $currentId = (int)$parentId;
            } else {
                break;
            }
        }
        return implode($separator, $ids);
    }

    public static function findParentNames(
        int    $pid,                        // 当前行的主键
        string $tableName,                  // 表名，无前缀
        string $separator = ',',            // 父子关系分隔符
        string $primaryKeyField = 'id',     // 主键字段
        string $parentIdField = 'pid',      // 父级字段
        string $nameField = 'name'          // 名称字段
    ): string
    {
        $names = [];
        $currentId = $pid;
        while ($currentId > 0) {
            $item = Db::name($tableName)
                ->where($primaryKeyField, $currentId)
                ->field([$parentIdField, $nameField]) // 只查询需要的字段
                ->find();
            if ($item) {
                if(isset($item[$nameField])) {
                    array_unshift($names, $item[$nameField]);
                }
                if (isset($item[$parentIdField]) && $item[$parentIdField] > 0) {
                    $currentId = $item[$parentIdField];
                } else {
                    break; // 没有父级ID或父级ID为0，停止循环
                }
            } else {
                break; // 未找到当前ID对应的记录，停止循环
            }
        }
        return implode($separator, $names);
    }

    public static function buildTree(
        array  $nodes,                  // 节点数据
        bool   $alwaysChildren = true,  // 节点数据中，没有父节点的节点，是否作为根节点
        int    $root = 0,               // 根节点ID
        string $primaryKey = 'id',      // 主键字段名
        string $foreignKey = 'pid',     // 外键字段名
        string $childrenKey = 'children' // 子节点字段名
    ): array
    {
        // 如果节点数组为空，直接返回空数组
        if (empty($nodes)) {
            return [];
        }

        // 创建一个哈希表来存储所有节点，以便快速查找
        $lookup = [];
        foreach ($nodes as $node) {
            // 检查节点是否包含必要的键
            if (!isset($node[$primaryKey]) || !isset($node[$foreignKey])) {
                throw new \InvalidArgumentException("Node is missing required keys: {$primaryKey} or {$foreignKey}");
            }
            $lookup[$node[$primaryKey]] = $node;
        }

        // 创建一个数组来存储根节点
        $tree = [];

        // 遍历所有节点，构建树结构
        foreach ($nodes as $node) {
            // 如果当前节点是根节点，直接将其加入树中
            if ($node[$foreignKey] === $root) {
                $tree[] = $node;
            } else {
                // 否则，找到其父节点并将其作为子节点加入
                $parentId = $node[$foreignKey];
                if (isset($lookup[$parentId])) {
                    // 如果父节点存在，确保父节点有 children 键
                    if (!isset($lookup[$parentId][$childrenKey])) {
                        $lookup[$parentId][$childrenKey] = [];
                    }
                    // 将当前节点加入父节点的 children 中
                    $lookup[$parentId][$childrenKey][] = $node;
                } else {
                    // 如果父节点不存在，可以选择抛出异常或记录警告（根据需求选择）
                    // throw new InvalidArgumentException("Parent node with ID {$parentId} not found");
                }
            }
        }

        // 递归为每个根节点添加子节点
        return self::addChildrenRecursively($tree, $lookup, $alwaysChildren, $primaryKey, $childrenKey);
    }

    /**
     * 递归为每个节点添加子节点
     *
     * @param array $nodes 当前层级的节点数组
     * @param array $lookup 所有节点的哈希表
     * @param bool $alwaysChildren 每个节点是否都有 children
     * @param string $primaryKey 主键键名
     * @param string $childrenKey 子节点键名
     * @return array 返回带有子节点的树结构
     */
    private static function addChildrenRecursively(
        array  $nodes,
        array  $lookup,
        bool   $alwaysChildren,
        string $primaryKey,
        string $childrenKey
    ): array
    {
        foreach ($nodes as &$node) {
            // 确保每个节点都有 children 键，即使它为空
            if (!isset($node[$childrenKey]) && $alwaysChildren) {
                $node[$childrenKey] = [];
            }

            // 如果当前节点有子节点，递归为每个子节点添加子节点
            if (isset($lookup[$node[$primaryKey]]) && isset($lookup[$node[$primaryKey]][$childrenKey])) {
                $node[$childrenKey] = self::addChildrenRecursively(
                    $lookup[$node[$primaryKey]][$childrenKey],
                    $lookup,
                    $alwaysChildren,
                    $primaryKey,
                    $childrenKey
                );
            }
        }
        return $nodes;
    }

}